﻿using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace PointAndLine
{
	public partial class MainGUI : Form
	{
		/// <summary>
		/// 是否正在画点和线
		/// </summary>
		private bool isDrawing = false;

		/// <summary>
		/// 画的点的记录，前面两个构成线，最后一个构成点
		/// </summary>
		private Point[] points = new Point[3];

		/// <summary>
		/// 当前的点为第几个点，从0开始计数，到2为结束并重置
		/// </summary>
		private int currentIndex = 0;

		/// <summary>
		/// 窗体的画布
		/// </summary>
		private Graphics graphics;

		public MainGUI()
		{
			InitializeComponent();
			graphics = showArea.CreateGraphics();
			graphics.SmoothingMode = SmoothingMode.AntiAlias;
			graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
		}

		/// <summary>
		/// 向量叉乘
		/// </summary>
		/// <param name="vector1">向量1，用长度为2的点数组表示向量，第一个元素为起始点，第二个为终止点</param>
		/// <param name="vector2">向量2，用长度为2的点数组表示向量，第一个元素为起始点，第二个为终止点</param>
		/// <returns>叉乘结果</returns>
		private int vectorCrossProduct(Point[] vector1, Point[] vector2)
		{
			int x1 = vector1[1].X - vector1[0].X;
			int y1 = vector1[1].Y - vector1[0].Y;
			int x2 = vector2[1].X - vector2[0].X;
			int y2 = vector2[1].Y - vector2[0].Y;
			return x1 * y2 - x2 * y1;
		}

		/// <summary>
		/// 向量点乘
		/// </summary>
		///<param name = "vector1" > 向量1，用长度为2的点数组表示向量，第一个元素为起始点，第二个为终止点</param>
		/// <param name="vector2">向量2，用长度为2的点数组表示向量，第一个元素为起始点，第二个为终止点</param>
		/// <returns>点乘结果</returns>
		private int vectorPointProduct(Point[] vector1, Point[] vector2)
		{
			int x1 = vector1[1].X - vector1[0].X;
			int y1 = vector1[1].Y - vector1[0].Y;
			int x2 = vector2[1].X - vector2[0].X;
			int y2 = vector2[1].Y - vector2[0].Y;
			return x1 * x2 + y1 * y2;
		}

		/// <summary>
		/// 获取一个线段的长度平方
		/// </summary>
		/// <param name="vector">向量，用长度为2的点数组表示向量，第一个元素为起始点，第二个为终止点</param>
		/// <returns>线段长度</returns>
		private int getLengthSquare(Point[] vector)
		{
			int x = vector[1].X - vector[0].X;
			int y = vector[1].Y - vector[0].Y;
			return x * x + y * y;
		}

		private void start_Click(object sender, System.EventArgs e)
		{
			isDrawing = true;
			start.Enabled = false;
			showArea.Refresh();
			tip.Text = "请点击下面控件中任意位置两个点以作一条线段";
			tip.ForeColor = Color.Red;
		}

		private void showArea_MouseClick(object sender, MouseEventArgs e)
		{
			if (isDrawing)
			{
				Pen pen = new Pen(Color.Purple, 2);
				Brush lineBrush = new SolidBrush(Color.DeepSkyBlue);
				Brush pointBrush = new SolidBrush(Color.Red);
				Size pointSize = new Size(4, 4);
				// 绘制点
				points[currentIndex] = new Point(e.X, e.Y);
				currentIndex++;
				if (currentIndex <= 2)
				{
					// 显示点和实际点位置作一点点偏移以显示不出现异常
					Point point = new Point(points[currentIndex - 1].X - pointSize.Width / 2, points[currentIndex - 1].Y - pointSize.Height / 2);
					graphics.FillEllipse(lineBrush, new Rectangle(point, pointSize));
				}
				// 点到两个点时，绘制线段
				if (currentIndex == 2)
				{
					graphics.DrawLine(pen, points[0], points[1]);
					tip.Text = "请点击下面控件中任意位置表示一个点";
					tip.ForeColor = Color.Blue;
				}
				// 点到第三个点时，停止绘制并计算第三个点和线段的位置关系
				if (currentIndex == 3)
				{
					// 绘制第三个点
					// 显示点和实际点位置作一点点偏移以显示不出现异常
					Point point = new Point(points[currentIndex - 1].X - pointSize.Width / 2, points[currentIndex - 1].Y - pointSize.Height / 2);
					graphics.FillEllipse(pointBrush, new Rectangle(point, pointSize));
					// 重置控件状态
					isDrawing = false;
					currentIndex = 0;
					start.Enabled = true;
					// 判断位置，用Point数组表示向量
					// 线段
					Point[] line = { points[0], points[1] };
					// 线段起点和点的向量
					Point[] lineStartToPoint = { points[0], points[2] };
					// 执行叉乘
					int result = vectorCrossProduct(line, lineStartToPoint);
					tip.ForeColor = Color.OrangeRed;
					if (result < 0)
					{
						tip.Text = "点在线段左侧";
					}
					else if (result > 0)
					{
						tip.Text = "点在线段右侧";
					}
					else if (result == 0)
					{
						// 共线，进一步判断
						int pointProduct = vectorPointProduct(line, lineStartToPoint);
						if (pointProduct < 0)
						{
							tip.Text = "点和线段共线但是在反向延长线上";
						}
						else
						{
							// 点乘值位于0和线段长度平方之间，则在线段上，否则在正向延长线上
							int lineLengthSquare = getLengthSquare(line);
							if (pointProduct <= lineLengthSquare)
							{
								tip.Text = "点在线段上";
							}
							else
							{
								tip.Text = "点和线段共线但是在正向延长线上";
							}
						}
					}
				}
			}
		}
	}
}